import 'package:dio/dio.dart';
import 'package:fluent_ui/fluent_ui.dart';

import '../api/auth_api.dart';
import '../constants.dart';
import '../global_setting.dart';
import '../model/auth_model.dart';
import '../model/result_model.dart';
import '../model/setting_model.dart';
import 'event_bus.dart';

enum HttpMethod {
  ///GET请求
  get("GET"),

  ///POST请求
  post("POST"),

  ///PUT请求
  put("PUT"),

  ///DELETE请求
  delete("DELETE");

  final String name;

  const HttpMethod(this.name);

  static HttpMethod parse(final String name) {
    if (name.isNotEmpty) {
      for (final HttpMethod method in HttpMethod.values) {
        if (method.name == name) {
          return method;
        }
      }
    }
    return HttpMethod.get;
  }
}

/// HTTP工具类
class Request {
  static const _connectTimeout = Duration(milliseconds: 20 * 1000);
  static const _sendTimeout = Duration(milliseconds: 20 * 1000);
  static const _receiveTimeout = Duration(milliseconds: 20 * 1000);
  static const _headerAuthorization = "Authorization";
  static const _headers = <String, dynamic>{
    'Accept': 'application/json,*/*',
    'Content-Type': 'application/json',
  };

  final Dio _dio;

  Request._() : _dio = _initDao();

  static final Request _singleton = Request._();

  factory Request() => _singleton;

  static Dio _initDao() => Dio(BaseOptions(
        connectTimeout: _connectTimeout,
        receiveTimeout: _receiveTimeout,
        headers: _headers,
        baseUrl: globalSetting.baseUrl,
      ));

  void _buildTokenHandler(final Options opts) {
    final at = globalSetting.accessToken;
    if (at.isNotEmpty) {
      final headers = opts.headers ?? {};
      headers[_headerAuthorization] = 'Bearer $at';
      opts.headers = headers;
    }
  }

  Future<Response> _createRequestHandler({
    required final HttpMethod method,
    required final String path,
    final bool useToken = true,
    final Map<String, dynamic>? params,
    final Map<String, dynamic>? data,
    final CancelToken? cancelToken,
    final ProgressCallback? onSendProgress,
    final ProgressCallback? onReceiveProgress,
  }) {
    //请求配置
    final options = Options(method: method.name);
    if (data != null && data.isNotEmpty) {
      options.sendTimeout = _sendTimeout;
    }
    //令牌
    if (useToken) {
      _buildTokenHandler(options);
    }
    //检查请求地址
    if (_dio.options.baseUrl.isEmpty) {
      _dio.options.baseUrl = globalSetting.baseUrl;
    }
    //拦截器
    final interceptors = _dio.interceptors;
    if (interceptors.isEmpty || interceptors.length < 2) {
      debugPrint('interceptors=>length:${interceptors.length}');
      final adds = <Interceptor>[
        //日志拦截器
        LogInterceptor(requestBody: true, responseBody: true),
        //刷新日志拦截器
        _RefreshTokenInterceptor(request: this),
      ];
      if (interceptors.isEmpty) {
        interceptors.addAll(adds);
      } else {
        final rows = interceptors.toList().where(
              (o) => (o is LogInterceptor) || (o is _RefreshTokenInterceptor),
            );
        if (rows.isEmpty) {
          interceptors.addAll(adds);
        }
      }
    }
    Map<String, dynamic>? parseIgnore(final Map<String, dynamic>? map) {
      if (map != null && map.isNotEmpty) {
        var data = <String, dynamic>{};
        map.forEach((key, value) {
          if (value != null) {
            data[key] = value;
          }
        });
        return data;
      }
      return null;
    }

    return _dio.request(
      path,
      data: parseIgnore(data),
      queryParameters: parseIgnore(params),
      cancelToken: cancelToken,
      options: options,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );
  }

  ///请求处理
  Future<ResultModelRes> requestHandler({
    required final HttpMethod method,
    required final String path,
    final bool useToken = true,
    final Map<String, dynamic>? params,
    final Map<String, dynamic>? data,
    final CancelToken? cancelToken,
    final ProgressCallback? onSendProgress,
    final ProgressCallback? onReceiveProgress,
  }) =>
      _createRequestHandler(
        method: method,
        path: path,
        useToken: useToken,
        params: params,
        data: data,
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      ).then((resp) {
        //404
        if (resp.statusCode == constants.netStatus404) {
          return ResultModelRes(
            retCode: constants.netStatus404,
            retMessage: "404",
          );
        }
        //异常
        if (resp.statusCode != 200) {
          final int code = resp.statusCode ?? constants.netStatusFail;
          return ResultModelRes(
            retCode: code,
            retMessage: resp.statusMessage ?? '服务器错误',
          );
        }
        final Map<String, dynamic> retMap = resp.data ?? {};
        if (retMap.isEmpty) {
          return ResultModelRes(
            retCode: constants.netStatusEmpty,
            retMessage: "未响应数据",
          );
        }
        //解析处理
        final ret = ResultModelRes.fromMap(retMap);
        if (!ret.isSuccessed && ret.retCode == constants.notAuthCode) {
          //触发认证失败事件总线
          eventBus.emit(constants.notAuthCode);
        }
        return ret;
      }).onError<DioException>((e, _) {
        final statusCode = e.response?.statusCode;
        debugPrint('[错误码:$statusCode]dio-exp: $e');
        final type = e.type;
        if (type == DioExceptionType.connectionTimeout ||
            type == DioExceptionType.sendTimeout ||
            type == DioExceptionType.receiveTimeout) {
          return ResultModelRes(
            retCode: constants.netStatusTimeout,
            retMessage: "超时",
          );
        }
        final message = '[$statusCode]${e.error.toString()}';
        return ResultModelRes(
          retCode: constants.netStatusFail,
          retMessage: message,
        );
      });

  ///GET请求处理
  Future<ResultModelRes> getHandler({
    required final String path,
    final bool useToken = true,
    final Map<String, dynamic>? params,
    final CancelToken? cancelToken,
    final ProgressCallback? onReceiveProgress,
  }) =>
      requestHandler(
        method: HttpMethod.get,
        path: path,
        useToken: useToken,
        params: params,
        cancelToken: cancelToken,
        onReceiveProgress: onReceiveProgress,
      );

  ///POST请求处理
  Future<ResultModelRes> postHandler({
    required final String path,
    final bool useToken = true,
    final Map<String, dynamic>? params,
    final Map<String, dynamic>? data,
    final CancelToken? cancelToken,
    final ProgressCallback? onSendProgress,
    final ProgressCallback? onReceiveProgress,
  }) =>
      requestHandler(
        method: HttpMethod.post,
        path: path,
        useToken: useToken,
        params: params,
        data: data,
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      );

  ///PUT请求处理
  Future<ResultModelRes> putHandler({
    required final String path,
    final bool useToken = true,
    final Map<String, dynamic>? params,
    final Map<String, dynamic>? data,
    final CancelToken? cancelToken,
    final ProgressCallback? onSendProgress,
    final ProgressCallback? onReceiveProgress,
  }) =>
      requestHandler(
        method: HttpMethod.put,
        path: path,
        useToken: useToken,
        params: params,
        data: data,
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      );

  ///DELETE请求处理
  Future<ResultModelRes> deleteHandler({
    required final String path,
    final bool useToken = true,
    final Map<String, dynamic>? params,
    final Map<String, dynamic>? data,
    final CancelToken? cancelToken,
    final ProgressCallback? onSendProgress,
    final ProgressCallback? onReceiveProgress,
  }) =>
      requestHandler(
        method: HttpMethod.delete,
        path: path,
        useToken: useToken,
        params: params,
        data: data,
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      );

  ///下载请求处理
  Future<bool> download({
    required final String path,
    required final String savePath,
    final bool useToken = true,
    final Map<String, dynamic>? params,
    final CancelToken? cancelToken,
    final ProgressCallback? onReceiveProgress,
  }) async {
    var options = Options(method: HttpMethod.get.name);
    if (useToken) {
      _buildTokenHandler(options);
    }
    final resp = await _dio.download(
      path,
      savePath,
      queryParameters: params,
      cancelToken: cancelToken,
      options: options,
      onReceiveProgress: onReceiveProgress,
    );
    return resp.statusCode == constants.netSuccessful;
  }
}

//刷新拦截器
class _RefreshTokenInterceptor extends Interceptor {
  final Request request;
  int _expireCount = 0;

  _RefreshTokenInterceptor({required this.request});

  @override
  void onResponse(
    final Response res,
    final ResponseInterceptorHandler handler,
  ) async {
    final Map<String, dynamic> dataMap = res.data ?? {};
    var ret = ResultModelRes.fromMap(dataMap);
    final path = res.requestOptions.path;
    debugPrint(
        'refreshTokenInterceptor[path:$path]=>${ret.retCode}#$_expireCount');
    if (ret.retCode == constants.tokenExpireCode) {
      //过期次数
      if (_expireCount > constants.expiredMax) {
        //刷新令牌失败
        final ok = await globalSetting.cleanToken();
        debugPrint('cleanToken[count:$_expireCount,path:$path]=>$ok');
        if(ok){
          //触发未认证事件总线
          eventBus.emit(constants.notAuthCode);
        }
        ret = ResultModelRes(
          retCode: constants.notAuthCode,
          retMessage: '',
        );
        handler.next(Response<Map<String, dynamic>>(
          data: ret.toMap,
          requestOptions: res.requestOptions,
        ));
        return;
      }
      //过期递增
      _expireCount += 1;
      //刷新令牌处理
      final ok = await refreshTokenRequestHandler();
      debugPrint('refreshToken[count: $_expireCount][ret:$ok] => $path');
      //刷新令牌成功
      if (ok) {
        _expireCount = 0;
        //重新请求处理
        final resp = await reconnectRequestHandler(res.requestOptions);
        //后续处理
        super.onResponse(resp, handler);
        return;
      }
    }
    super.onResponse(res, handler);
  }

  Future<bool> refreshTokenRequestHandler() => authApi
          .refresh(RefreshTokenReq(refreshToken: globalSetting.refreshToken))
          .then((res) {
        if (res.isSuccessed) {
          return globalSetting.saveLocal(SettingModel(
            accessToken: res.accessToken,
            refreshToken: res.refreshToken,
          ));
        }
        return false;
      });

  Future<Response> reconnectRequestHandler(final RequestOptions opts) =>
      request._createRequestHandler(
        method: HttpMethod.parse(opts.method),
        path: opts.path,
        useToken: true,
        params: opts.queryParameters,
        data: opts.data,
        cancelToken: opts.cancelToken,
        onSendProgress: opts.onSendProgress,
        onReceiveProgress: opts.onReceiveProgress,
      );
}

/// HTTP工具类单例
final request = Request();
